home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
dev
/
devTty.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
22KB
|
811 lines
/*
* devTty.c --
*
* This file implements terminals and terminal-like devices for
* Sprite. It consists of a bunch of glue that hooks together a
* generic terminal driver (the Td_ library), one or more
* machine- and device-specific drivers for RS232 hardware,
* and the generic Sprite kernel-call interfaces for devices.
*
* Copyright 1989 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/devTty.c,v 9.8 90/10/11 17:13:01 mendel Exp $ SPRITE (Berkeley)";
#endif /* not lint */
#include <sprite.h>
#include <stdio.h>
#include <dev.h>
#include <ttyAttach.h>
#include <errno.h>
#include <fs.h>
#include <fsio.h>
#include <proc.h>
#include <status.h>
#include <sync.h>
#include <tty.h>
#include <td.h>
#include <compatInt.h>
#include <fsioDevice.h>
/*
* Monitor lock used for synchronizing access to DevTty and Td_Terminal
* structures. See the comment above the declaration for DevTty structs
* (in devTty.h) for more information. This lock may also be used by
* a few other files that interact closely with this one.
*/
Sync_Lock devTtyLock = Sync_LockInitStatic("Dev:devTtyLock");
#define LOCKPTR (&devTtyLock)
/*
* The following variable tells when the last user interaction occurred
* on the console. It doesn't exactly belong here, but there isn't
* anyplace where it fits better.
*/
Time dev_LastConsoleInput;
/*
* Forward declarations for procedures defined in this file:
*/
static int CookedProc();
static int RawProc();
static void Signal();
static void Transfer();
static void TransferInProc();
static void TransferOutProc();
/*
*----------------------------------------------------------------------
*
* DevTtyOpen --
*
* Called through devFsOpTable to open a terminal device.
* Initializes the device and activates it so that it's ready
* to return input.
*
* Results:
* A standard Sprite ReturnStatus.
*
* Side effects:
* The device will be "turned on" if it isn't already, which may
* involve setting up interrupt handlers.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ENTRY ReturnStatus
DevTtyOpen(devicePtr, useFlags, notifyToken, flagsPtr)
Fs_Device *devicePtr; /* Information about device (e.g. type
* and unit number). */
int useFlags; /* Flags for the stream being opened:
* OR'ed combination of FS_READ and
* FS_WRITE. */
Fs_NotifyToken notifyToken; /* Used for Fs call-back to notify waiting
* processes that the terminal is ready. */
int *flagsPtr; /* OUT: Device IO flags */
{
register DevTty *ttyPtr;
int result;
LOCK_MONITOR;
/*
* Call machine-specific code to attach to the device, then fill in
* all the fields that weren't already filled in by the attach procedure.
*/
ttyPtr = DevTtyAttach(devicePtr->unit);
if (ttyPtr == NULL) {
UNLOCK_MONITOR;
return Compat_MapToSprite(ENXIO);
}
if (ttyPtr->openCount == 0) {
ttyPtr->insertInput = 0;
ttyPtr->extractInput = 0;
ttyPtr->insertOutput = 0;
ttyPtr->extractOutput = 0;
ttyPtr->term = Td_Create(500, CookedProc, (ClientData) ttyPtr,
RawProc, (ClientData) ttyPtr);
ttyPtr->notifyToken = notifyToken;
ttyPtr->openCount = 0;
(*ttyPtr->activateProc)(ttyPtr->rawData);
} else if (ttyPtr->notifyToken != notifyToken) {
/*
* The console device is opened during boot time, at which
* time the proper notifyToken (i.e., a file system handle)
* is not available. Here we reset the notifyToken to ensure
* proper communication between the device driver and the
* higher-level FS code. See Fsio_BootTimeTtyOpen.
*/
ttyPtr->notifyToken = notifyToken;
}
/*
* Officially open the terminal.
*/
result = Td_Open(ttyPtr->term, &ttyPtr->selectState);
if (result != 0) {
if (ttyPtr->openCount == 0) {
Td_Delete(ttyPtr->term);
}
UNLOCK_MONITOR;
return Compat_MapToSprite(result);
}
ttyPtr->openCount++;
devicePtr->data = (ClientData) ttyPtr;
UNLOCK_MONITOR;
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* DevTtyRead --
*
* Called through devFsOpTable to read from a terminal device.
*
* Results:
* A standard Sprite ReturnStatus. Characters are stored at
* *readPtr->buffer, and the fields of *replyPtr are modified
* to describe what happened.
*
* Side effects:
* Information may be removed from the input buffer for the terminal.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ENTRY ReturnStatus
DevTtyRead(devicePtr, readPtr, replyPtr)
Fs_Device *devicePtr; /* Information about device. */
register Fs_IOParam *readPtr; /* Input parameters. */
register Fs_IOReply *replyPtr; /* Place to store return information. */
{
register DevTty *ttyPtr = (DevTty *) devicePtr->data;
int result;
LOCK_MONITOR;
replyPtr->length = readPtr->length;
result = Td_GetCooked(ttyPtr->term, readPtr->procID, readPtr->familyID,
&replyPtr->length, (char *) readPtr->buffer, &replyPtr->signal,
&ttyPtr->selectState);
replyPtr->flags = ttyPtr->selectState;
UNLOCK_MONITOR;
if (result != 0) {
return Compat_MapToSprite(result);
}
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* DevTtyWrite --
*
* Called through devFsOpTable to write to a terminal device.
*
* Results:
* A standard Sprite ReturnStatus. Fields of *replyPtr are
* modified to indicate what happened in the write operation.
*
* Side effects:
* Information may be added to the output buffer for the terminal.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ENTRY ReturnStatus
DevTtyWrite(devicePtr, writePtr, replyPtr)
Fs_Device *devicePtr; /* Information about device. */
register Fs_IOParam *writePtr; /* Input parameters. */
register Fs_IOReply *replyPtr; /* Place to store result info. */
{
register DevTty *ttyPtr = (DevTty *) devicePtr->data;
int result, countThisTime, stillToDo;
char *bufPtr;
ReturnStatus status = SUCCESS;
LOCK_MONITOR;
/*
* Td_PutCooked will accept anything we give it even if it exceeds
* the nominal buffer size. To keep it from growing its output buffer
* very large, break big blocks up into small ones, and stop as soon
* as the FS_WRITABLE bit goes away (which means the nominal buffer
* size has been exceeded).
*/
replyPtr->length = 0;
stillToDo = writePtr->length;
bufPtr = (char *) writePtr->buffer;
while (stillToDo != 0) {
if (!(ttyPtr->selectState & FS_WRITABLE)) {
status = FS_WOULD_BLOCK;
break;
}
countThisTime = stillToDo;
if (countThisTime > 100) {
countThisTime = 100;
}
result = Td_PutCooked(ttyPtr->term, &countThisTime, bufPtr,
&replyPtr->signal, &ttyPtr->selectState);
replyPtr->flags = ttyPtr->selectState;
replyPtr->length += countThisTime;
bufPtr += countThisTime;
stillToDo -= countThisTime;
if (result != 0) {
status = Compat_MapToSprite(result);
break;
}
}
UNLOCK_MONITOR;
return status;
}
/*
*----------------------------------------------------------------------
*
* DevTtyIOControl --
*
* Called through devFsOpTable to perform IOControl operations on
* a terminal device.
*
* Results:
* A standard Sprite ReturnStatus. *iocPtr->outBuffer may be
* modified to hold the output information from the IOControl,
* and the fields of *replyPtr are modified to show what happened.
*
* Side effects:
* Depends on the IOControl operation.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ENTRY ReturnStatus
DevTtyIOControl(devicePtr, iocPtr, replyPtr)
Fs_Device *devicePtr; /* Information about device. */
register Fs_IOCParam *iocPtr; /* Parameter information (buffer sizes
* etc.). */
register Fs_IOReply *replyPtr; /* Place to store result information. */
{
register DevTty *ttyPtr = (DevTty *) devicePtr->data;
int result;
LOCK_MONITOR;
replyPtr->length = iocPtr->outBufSize;
result = Td_ControlCooked(ttyPtr->term, iocPtr->command, iocPtr->format,
iocPtr->inBufSize, (char *) iocPtr->inBuffer,
&replyPtr->length, (char *) iocPtr->outBuffer,
&replyPtr->signal, &ttyPtr->selectState);
replyPtr->flags = ttyPtr->selectState;
UNLOCK_MONITOR;
if (result != 0) {
return Compat_MapToSprite(result);
}
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* DevTtySelect --
*
* Called through devFsOpTable to perform select-related functions
* on a terminal device.
*
* Results:
* Always SUCCESS. The values at *readPtr, *writePtr, and *exceptPtr
* get set to zero if the device is NOT readable, or writable, or
* exception-pending, respectively.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
DevTtySelect(devicePtr, readPtr, writePtr, exceptPtr)
Fs_Device *devicePtr; /* Information about device. */
int *readPtr; /* Set to zero if device not readable. */
int *writePtr; /* Set to zero if device not writable. */
int *exceptPtr; /* Set to zero if no exception pending on
* device. */
{
register DevTty *ttyPtr = (DevTty *) devicePtr->data;
/*
* Because this is only a simple query, don't need any locking in
* this procedure.
*/
if (!(ttyPtr->selectState & FS_READABLE)) {
*readPtr = 0;
}
if (!(ttyPtr->selectState & FS_WRITABLE)) {
*writePtr = 0;
}
if (!(ttyPtr->selectState & FS_EXCEPTION)) {
*exceptPtr = 0;
}
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* DevTtyClose --
*
* Called through devFsOpTable whenever a terminal is closed.
*
* Results:
* A standard Sprite return status.
*
* Side effects:
* Data structures get cleaned up and possibly deallocated.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ENTRY ReturnStatus
DevTtyClose(devicePtr, useFlags, openCount, writerCount)
Fs_Device *devicePtr; /* Information about device. */
int useFlags; /* Indicates whether stream being
* closed was open for reading and/or
* writing: OR'ed combination of
* FS_READ and FS_WRITE. */
int openCount; /* # of times this particular stream
* is still open. */
int writerCount; /* # of times this particular stream
* is still open for writing. */
{
DevTty *ttyPtr = (DevTty *) devicePtr->data;
if (openCount > 0) {
return SUCCESS;
}
LOCK_MONITOR;
Td_Close(ttyPtr->term);
ttyPtr->openCount --;
if (ttyPtr->openCount == 0) {
Td_Delete(ttyPtr->term);
}
UNLOCK_MONITOR;
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* CookedProc --
*
* This procedure is called by the Td library to perform control
* operations on the "cooked" size of the terminal.
*
* Results:
* Always zero, to indicate that no information is returned
* in outBuffer.
*
* Side effects:
* Depends on the control operation. Most likely effect is to
* wake up a waiting process.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
INTERNAL static int
CookedProc(ttyPtr, operation, inBufSize, inBuffer, outBufSize, outBuffer)
register DevTty *ttyPtr; /* Our information about terminal. */
int operation; /* What to do: TD_COOKED_SIGNAL, etc. */
int inBufSize; /* Size of input buffer for operation. */
char *inBuffer; /* Input buffer. */
int outBufSize; /* Size of output buffer for operation. */
char *outBuffer; /* Output buffer. */
{
int result = 0;
int sigNum, pID;
switch (operation) {
case TD_COOKED_SIGNAL:
sigNum = ((int *) inBuffer)[0];
pID = ((int *) inBuffer)[1];
Signal(sigNum, (Proc_PID) pID, TRUE);
break;
case TD_COOKED_READS_OK:
if (!(ttyPtr->selectState & FS_READABLE)) {
ttyPtr->selectState |= FS_READABLE;
Fsio_DevNotifyReader(ttyPtr->notifyToken);
}
break;
case TD_COOKED_WRITES_OK:
if (!(ttyPtr->selectState & FS_WRITABLE)) {
ttyPtr->selectState |= FS_WRITABLE;
Fsio_DevNotifyWriter(ttyPtr->notifyToken);
}
break;
}
return result;
}
/*
*----------------------------------------------------------------------
*
* RawProc --
*
* This procedure is called by the Td library to perform control
* operations on the "raw" size of the terminal.
*
* Results:
* The return value is the number of bytes returned to the caller
* at outBuffer.
*
* Side effects:
* Depends on the control operation. Most likely effect is to
* start transferring output data.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
INTERNAL static int
RawProc(ttyPtr, operation, inBufSize, inBuffer, outBufSize, outBuffer)
register DevTty *ttyPtr; /* Our information about terminal. */
int operation; /* What to do: TD_RAW_OUTPUT_READY etc. */
int inBufSize; /* Size of input buffer for operation. */
char *inBuffer; /* Input buffer. */
int outBufSize; /* Size of output buffer for operation. */
char *outBuffer; /* Output buffer. */
{
/*
* Move data to the intermediate output buffer, if it's ready.
* Otherwise pass the operation on to the device-level procedure.
*/
if (operation == TD_RAW_OUTPUT_READY) {
Transfer(ttyPtr);
return 0;
}
/*
* Don't actually close the device if it's the console: otherwise
* we'd stop responding to console commands.
*/
if ((operation == TD_RAW_SHUTDOWN)
&& (ttyPtr->consoleFlags & DEV_TTY_IS_CONSOLE)) {
return 0;
}
return ttyPtr->rawProc(ttyPtr->rawData, operation, inBufSize, inBuffer,
outBufSize, outBuffer);
}
/*
*----------------------------------------------------------------------
*
* DevTtyInputChar --
*
* This procedure is invoked at interrupt level by device drivers
* when an input character is received.
*
* Results:
* None.
*
* Side effects:
* The character is added to the buffer for ttyPtr, and arrangements
* are made to process the character more fully in a process later.
* If the buffer overflows, an error message is printed on the console.
*
*----------------------------------------------------------------------
*/
void
DevTtyInputChar(ttyPtr, value)
register DevTty *ttyPtr; /* Our information about terminal. */
int value; /* Character (or special condition like break)
* that just arrived on terminal. */
{
int next, old;
old = ttyPtr->insertInput;
next = old+1;
if (next >= TTY_IN_BUF_SIZE) {
next = 0;
}
if (next == ttyPtr->extractInput) {
if (!(ttyPtr->consoleFlags & DEV_TTY_OVERFLOWED)) {
ttyPtr->consoleFlags |= DEV_TTY_OVERFLOWED;
printf("Buffer overflow in DevTtyInputChar\n");
}
return;
}
ttyPtr->inBuffer[old] = value;
/*
* Note: must advance insertInput before scheduling background
* procedure, in order to avoid race.
*/
ttyPtr->insertInput = next;
if (old == ttyPtr->extractInput) {
Proc_CallFunc(TransferInProc, (ClientData) ttyPtr, 0);
}
}
/*
*----------------------------------------------------------------------
*
* TransferInProc --
*
* This procedure is invoked in a kernel server process in response
* to a Proc_CallFunc arrangement made by DevTtyInputChar.
*
* Results:
* None.
*
* Side effects:
* Characters get removed from ttyPtr->inBuffer and sent off to the
* Td library.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ENTRY static void
TransferInProc(ttyPtr, callInfoPtr)
register DevTty *ttyPtr; /* Our information about terminal. */
Proc_CallInfo *callInfoPtr; /* Not used. */
{
int value, next;
char c;
LOCK_MONITOR;
/*
* If the terminal is no longer open then don't do anything: there's
* no Td_Terminal to do it on.
*/
if (ttyPtr->openCount == 0) {
UNLOCK_MONITOR;
return;
}
while (ttyPtr->extractInput != ttyPtr->insertInput) {
value = ttyPtr->inBuffer[ttyPtr->extractInput];
next = ttyPtr->extractInput + 1;
if (next >= TTY_IN_BUF_SIZE) {
next = 0;
}
ttyPtr->extractInput = next;
if (ttyPtr->inputProc != (void (*)()) NIL) {
(*ttyPtr->inputProc)(ttyPtr->inputData, value);
} else {
if (value == DEV_TTY_BREAK) {
if (ttyPtr->consoleFlags & DEV_TTY_IS_CONSOLE) {
ttyPtr->consoleFlags |= DEV_TTY_GOT_BREAK;
} else {
Td_ControlRaw(ttyPtr->term, TD_BREAK);
}
} else {
if (value == DEV_TTY_HANGUP) {
Td_ControlRaw(ttyPtr->term, TD_LOST_CARRIER);
} else if ((ttyPtr->consoleFlags
& (DEV_TTY_GOT_BREAK|DEV_TTY_IS_CONSOLE))
== (DEV_TTY_GOT_BREAK|DEV_TTY_IS_CONSOLE)) {
Dev_InvokeConsoleCmd(value);
} else {
c = value;
Td_PutRaw(ttyPtr->term, 1, &c);
}
ttyPtr->consoleFlags &= ~DEV_TTY_GOT_BREAK;
}
}
if (ttyPtr->consoleFlags & DEV_TTY_IS_CONSOLE) {
Timer_GetTimeOfDay(&dev_LastConsoleInput, (int *) NIL,
(Boolean *) NIL);
}
}
ttyPtr->consoleFlags &= ~DEV_TTY_OVERFLOWED;
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* DevTtyOutputChar --
*
* This procedure is called at interrupt level to retrieve the
* next character ready for output, if there are any.
*
* Results:
* The return value is either the next ASCII character ready
* for output (returned unsigned in an int), or -1 to indicate
* that the output buffer is empty.
*
* Side effects:
* Characters are removed from ttyPtr's outBuffer, and a background-
* level procedure call may be scheduled to refill the buffer.
*
*----------------------------------------------------------------------
*/
int
DevTtyOutputChar(ttyPtr)
register DevTty *ttyPtr; /* Information about the terminal. */
{
int result, next;
if (ttyPtr->extractOutput == ttyPtr->insertOutput) {
return -1;
}
result = ttyPtr->outBuffer[ttyPtr->extractOutput];
next = ttyPtr->extractOutput + 1;
if (next >= TTY_OUT_BUF_SIZE) {
next = 0;
}
ttyPtr->extractOutput = next;
if (ttyPtr->extractOutput == ttyPtr->insertOutput) {
Proc_CallFunc(TransferOutProc, (ClientData) ttyPtr, 0);
}
return result;
}
/*
*----------------------------------------------------------------------
*
* TransferOutProc --
*
* This procedure is invoked in a kernel server process in response
* to a Proc_CallFunc arrangement made by DevTtyOutputChar.
*
* Results:
* None.
*
* Side effects:
* Characters get transferred from Td's output buffer to the
* intermediate buffer for Td.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ENTRY static void
TransferOutProc(ttyPtr, callInfoPtr)
register DevTty *ttyPtr; /* Our information about terminal. */
Proc_CallInfo *callInfoPtr; /* Not used. */
{
LOCK_MONITOR;
/*
* Make sure that the terminal is still open. Otherwise its Td_Terminal
* will have gone away.
*/
if (ttyPtr->openCount != 0) {
Transfer(ttyPtr);
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* Transfer --
*
* Do all the real work of copying characters from the Td buffer
* for a terminal to the intermediate buffer in ttyPtr.
*
* Results:
* None.
*
* Side effects:
* Characters get copied to the intermediate buffer. If there's
* really anything to copy, then the device-level rawProc gets
* called to start up output.
*
*----------------------------------------------------------------------
*/
INTERNAL static void
Transfer(ttyPtr)
register DevTty *ttyPtr; /* Information about terminal. Caller must
* have locked ttyPtr. */
{
int count, next, tmp, oldDiff;
/*
* Transfer characters in the largest possible blocks such that each
* block fits in the buffer without wrap-around.
*/
oldDiff = ttyPtr->insertOutput - ttyPtr->extractOutput;
while (TRUE) {
count = ttyPtr->extractOutput - ttyPtr->insertOutput - 1;
if (count < 0) {
count += TTY_OUT_BUF_SIZE;
}
if (count > (TTY_OUT_BUF_SIZE - ttyPtr->insertOutput)) {
count = TTY_OUT_BUF_SIZE - ttyPtr->insertOutput;
}
if (count <= 0) {
break;
}
tmp = Td_GetRaw(ttyPtr->term, count,
(char *) &ttyPtr->outBuffer[ttyPtr->insertOutput]);
next = ttyPtr->insertOutput + tmp;
if (next >= TTY_OUT_BUF_SIZE) {
next = 0;
}
ttyPtr->insertOutput = next;
if (tmp < count) {
break;
}
}
/*
* Wake up the device if there didn't used to be characters in the
* buffer but there are now.
*/
if ((oldDiff == 0) && (ttyPtr->insertOutput != ttyPtr->extractOutput)) {
(*ttyPtr->rawProc)(ttyPtr->rawData, TD_RAW_OUTPUT_READY,
0, (char *) NIL, 0, (char *) NIL);
}
}
/*
*----------------------------------------------------------------------
*
* Signal --
*
* This is a utility procedure that converts a signal from UNIX to
* Sprite and sends it to a process or group.
*
* Results:
* None.
*
* Side effects:
* A signal get sent.
*
*----------------------------------------------------------------------
*/
static void
Signal(sigNum, id, family)
int sigNum; /* UNIX signal number to send. */
Proc_PID id; /* Id of process or family. */
Boolean family; /* TRUE means id is for family,
* FALSE means it's for process. */
{
int spriteSignal;
if (Compat_UnixSignalToSprite(sigNum, &spriteSignal) != SUCCESS) {
printf("Warning: %s %d to Sprite.\n",
"devTty.Signal couldn't translate sigNum", sigNum);
} else {
(void) Sig_Send(spriteSignal, SIG_NO_CODE, id, family, (Address)0);
}
}